package com.itaem.community.modules.infrastructure.user.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.baomidou.mybatisplus.extension.toolkit.SqlHelper;
import com.dtflys.forest.http.ForestResponse;
import com.itaem.community.core.entity.PageData;
import com.itaem.community.core.exception.CommunityException;
import com.itaem.community.core.exception.common.IllegalParameterException;
import com.itaem.community.core.exception.data.DataAlreadyExistsException;
import com.itaem.community.core.exception.data.DataSaveException;
import com.itaem.community.core.exception.data.NoSuchDataException;
import com.itaem.community.core.exception.remote.RemoteAPICallFailException;
import com.itaem.community.core.utils.PageUtils;
import com.itaem.community.modules.infrastructure.authority.mapper.role.RoleDAO;
import com.itaem.community.modules.infrastructure.company.company.entity.PropertyCompanyUserEntity;
import com.itaem.community.modules.infrastructure.company.company.mapper.PropertyCompanyUserDAO;
import com.itaem.community.modules.infrastructure.user.dto.*;
import com.itaem.community.modules.infrastructure.user.entity.UserEntity;
import com.itaem.community.modules.infrastructure.user.entity.UserPhoneEntity;
import com.itaem.community.modules.infrastructure.user.entity.UserWechatEntity;
import com.itaem.community.modules.infrastructure.user.enums.Gender;
import com.itaem.community.modules.infrastructure.user.mapper.UserDAO;
import com.itaem.community.modules.infrastructure.user.mapper.UserPhoneDAO;
import com.itaem.community.modules.infrastructure.user.mapper.UserTransformer;
import com.itaem.community.modules.infrastructure.user.mapper.UserWechatDAO;
import com.itaem.community.modules.infrastructure.user.service.UserService;
import com.itaem.community.modules.infrastructure.user.service.remote.UserAuthService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.crypto.hash.Sha256Hash;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Optional;

import static java.lang.Boolean.FALSE;
import static java.lang.Boolean.TRUE;

/**
 * <p>
 * 用户信息 服务实现类
 * </p>
 *
 * @author tomato
 * @since 2020-10-28
 */
@Slf4j
@Service
public class UserServiceImpl extends ServiceImpl<UserDAO, UserEntity> implements UserService {

    @Autowired
    private UserDAO userDAO;

    @Autowired
    private UserPhoneDAO userPhoneDAO;

    @Autowired
    private RoleDAO roleDAO;

    @Autowired
    private UserWechatDAO userWechatDAO;

    @Autowired
    private UserTransformer userTransformer;

    @Autowired
    private PropertyCompanyUserDAO propertyCompanyUserDAO;

    @Autowired
    private UserAuthService userAuthService;

    @Override
    public PageData<UserDTO> pageUsers(Long page, Long size) {
        return PageUtils.extract(userDAO.listUsers(new Page<>(page, size)));
    }

    @Transactional(rollbackFor = Exception.class)
    public UserEntity saveDefaultUserInfo(String name, Integer roleId, String gender) {
        UserEntity userEntity = new UserEntity();

        // 设置姓名
        userEntity.setName(name);
        // 赋予住户权限
        userEntity.setRoleId(
                Optional.ofNullable(roleDAO.selectById(roleId))
                        // 不存在则抛出异常
                        .orElseThrow(NoSuchDataException::new)
                        .getId()
        );
        // 设置性别
        if (StringUtils.isEmpty(gender)) {
            throw new IllegalParameterException("性别不能为空!");
        }
        String target = gender.toUpperCase();
        for (Gender g : Gender.values()) {
            if (g.getGender().equals(target)) {
                userEntity.setGender(g);
                break;
            }
        }
        // 默认未执行逻辑删除
        userEntity.setDeleted(FALSE);
        // 执行保存
        if (userDAO.insert(userEntity) != 1) {
            throw new DataSaveException("用户信息保存失败");
        }
        return userEntity;
    }

    @Override
    @Transactional(rollbackFor = CommunityException.class)
    public Boolean saveUserByPhoneNumber(UserPhoneCreateDTO dto) {
        // 利用unique键，确认手机号有无重复
        if (userPhoneDAO
                .selectCount(
                        new QueryWrapper<UserPhoneEntity>()
                                .lambda()
                                .eq(UserPhoneEntity::getPhoneNumber, dto.getPhoneNumber())) >= 1) {
            throw new DataAlreadyExistsException("此手机号已注册,请直接登录");
        }
        // 这里有三部分
        // 1.创建用户
        UserEntity userEntity = saveDefaultUserInfo(
                dto.getUsername(),
                dto.getRoleId(),
                dto.getGender()
        );

        // 2.然后创建用户的手机信息
        UserPhoneEntity userPhoneEntity = userTransformer.phoneDtoToPhoneEntity(dto);
        // 设置用户id，与之绑定
        userPhoneEntity.setUserId(userEntity.getId());
        userPhoneEntity.setSalt(RandomStringUtils.randomAlphanumeric(20));
        userPhoneEntity.setPassword(
                // 加密密码
                new Sha256Hash(dto.getPassword(), userPhoneEntity.getSalt()).toHex()
        );
        // 3.连接用户与物业公司
        PropertyCompanyUserEntity propertyCompanyUserEntity = new PropertyCompanyUserEntity();
        propertyCompanyUserEntity.setUserId(userEntity.getId());
        propertyCompanyUserEntity.setCompanyId(dto.getCompanyId());

        // 执行保存
        if (propertyCompanyUserDAO.insert(propertyCompanyUserEntity) != 1
                || userPhoneDAO.insert(userPhoneEntity) != 1) {
            throw new DataSaveException("注册用户失败[phone]");
        }
        return TRUE;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean saveUserByWechatNumber(UserWechatCreateDTO dto) {
        // 利用unique键，确认手机号有无重复
        if (userWechatDAO
                .selectCount(
                        new QueryWrapper<UserWechatEntity>()
                                .lambda()
                                .eq(UserWechatEntity::getOpenId, dto.getOpenId())) >= 1) {
            throw new DataAlreadyExistsException("此微信已注册，请直接登录");
        }
        // 思路和手机信息保存大同小异
        UserEntity userEntity = saveDefaultUserInfo(
                dto.getUsername(),
                dto.getRoleId(),
                dto.getGender()
        );

        // 连接用户与微信信息
        UserWechatEntity userWechatEntity = userTransformer.wechatDtoToWechatEntity(dto);
        userWechatEntity.setUserId(userEntity.getId());

        // 连接用户与物业公司
        PropertyCompanyUserEntity propertyCompanyUserEntity = new PropertyCompanyUserEntity();
        propertyCompanyUserEntity.setUserId(userEntity.getId());
        propertyCompanyUserEntity.setCompanyId(dto.getCompanyId());

        if (propertyCompanyUserDAO.insert(propertyCompanyUserEntity) != 1
                || userWechatDAO.insert(userWechatEntity) != 1) {
            throw new DataSaveException("注册用户失败[wechat]");
        }

        return TRUE;
    }

    @Override
    public UserDTO getUserById(Integer userId) {
        UserDTO dto = userDAO.getUser(userId);
        System.out.println(dto);
        return dto;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean updateUser(UserUpdateDTO updateDto) {
        if (updateDto.getId() == null) {
            throw new IllegalParameterException("更新时提交的id不能为空");
        }
        UserEntity entity = userTransformer.updateDtoToEntity(updateDto);
        // 由于mapstruct没有办法很好地完成gender这一enum的转换，遂手动完成
        if (StringUtils.isEmpty(updateDto.getGender())) {
            throw new IllegalParameterException("性别不能为空!");
        }
        String target = updateDto.getGender().toUpperCase();
        for (Gender gender : Gender.values()) {
            if (gender.getGender().equals(target)) {
                entity.setGender(gender);
                break;
            }
        }
        Optional.ofNullable(entity.getGender()).orElseThrow(IllegalParameterException::new);
        return SqlHelper.retBool(userDAO.updateById(entity));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean updatePhoneLoginPassword(UserPhoneUpdateDTO userPhoneUpdateDto) {
        // 判断旧密码是否与新密码相同
        if (StringUtils.equals(userPhoneUpdateDto.getOldPassword(), userPhoneUpdateDto.getNewPassword())) {
            throw new IllegalParameterException("新旧密码一致");
        }

        // 取出密码盐和数据库中记录的密码
        UserPhoneEntity user = userPhoneDAO.selectOne(
                new QueryWrapper<UserPhoneEntity>()
                        .lambda()
                        .eq(UserPhoneEntity::getUserId, userPhoneUpdateDto.getUserId())
                        .select(UserPhoneEntity::getPassword, UserPhoneEntity::getSalt)
        );

        log.info(new Sha256Hash(userPhoneUpdateDto.getOldPassword(), user.getSalt()).toHex());
        log.info(user.getPassword());

        // 判断旧密码是否相同
        if (
                !StringUtils.equals(
                        new Sha256Hash(userPhoneUpdateDto.getOldPassword(), user.getSalt()).toHex(),
                        user.getPassword()
                )
        ) {
            throw new DataSaveException("请检查旧密码是否正确");
        }

        // 组装实体
        UserPhoneEntity phoneEntity = new UserPhoneEntity();
        phoneEntity.setUserId(userPhoneUpdateDto.getUserId());
        // 使用salt加密新密码
        phoneEntity.setPassword(new Sha256Hash(userPhoneUpdateDto.getNewPassword(), user.getSalt()).toHex());
        // 执行更新
        return userPhoneDAO.update(
                phoneEntity,
                new QueryWrapper<UserPhoneEntity>()
                        .lambda()
                        .eq(UserPhoneEntity::getUserId, userPhoneUpdateDto.getUserId())
        ) == 1;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean idCardAuthenticate(UserIdCardAuthenticationDTO authenticationDto) {
        // 调用远程api进行实名认证
        ForestResponse<UserIdCardAuthenticationResultDTO> resp
                = userAuthService.tryAuthenticate(
                        authenticationDto.getIdCardNumber(),
                        authenticationDto.getName()
        );

        // 如果http状态码不为200
        if (resp.isError()) {
            // 更换接口之后需要在这里做返回
            throw new RemoteAPICallFailException(resp.getResult().getMessage());
        }

        UserIdCardAuthenticationResultDTO res = resp.getResult();

        // 判定result中的code，失败则结果不为"0"
        if (
                !UserIdCardAuthenticationResultDTO.INFO_EXIST.equals(
                        // 判空
                        Optional.ofNullable(res.getCode())
                                .orElseThrow(RemoteAPICallFailException::new)
                )
        ) {
            log.warn("无法查询到此身份证信息");
            return Boolean.FALSE;
        }

        // 二要素认证
        if (UserIdCardAuthenticationResultDTO.AUTH_SUCCESS.equals(res.getResult().getRes())) {
            // 数据创建
            UserEntity user = new UserEntity();
            user.setId(authenticationDto.getId());
            user.setIdCardNumber(res.getResult().getIdcard());
            user.setName(res.getResult().getName());
            // 更新数据
            userDAO.updateById(user);
            return Boolean.TRUE;
        } else {
            log.warn("身份信息匹配失败，或不存在此身份信息");
            return Boolean.FALSE;
        }
    }

    @Override
    @Transactional(rollbackFor = CommunityException.class)
    public Boolean removeUserById(Integer id) {
        userDAO.deleteById(id);
        return userDAO.selectCount(new QueryWrapper<UserEntity>().lambda().eq(UserEntity::getId, id)) == 0;
    }

}
